using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using JGLib.States;
using System.IO;
using System.IO.IsolatedStorage;
using TrafficTrouble.States;
using TrafficTrouble.Core;

namespace TrafficTrouble
{
#if WINDOWS || XBOX
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        static void Main(string[] args)
        {
            using (Game game = new Game())
            {
                game.Run();
            }
        }
    }
#endif

    public class Game : Microsoft.Xna.Framework.Game
    {
        public static Game TrafficTroubleGame;

        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        public static int Width, Height;

        private const string SAVE_GAME_FILE = "CURRENT_GAME.SAV";

        public const byte LEVEL_FORMAT_VERSION = 1;

        public enum Dialog { Reset, BackToMenu, BackToLevelSelection }

        public Game()
        {
            TrafficTroubleGame = this;

            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            IsMouseVisible = true;

            TargetElapsedTime = TimeSpan.FromTicks(333333);
            InactiveSleepTime = TimeSpan.FromSeconds(1);

            Microsoft.Xna.Framework.Input.Touch.TouchPanel.EnabledGestures
                = Microsoft.Xna.Framework.Input.Touch.GestureType.Tap
                | Microsoft.Xna.Framework.Input.Touch.GestureType.DoubleTap
                | Microsoft.Xna.Framework.Input.Touch.GestureType.Flick
                | Microsoft.Xna.Framework.Input.Touch.GestureType.VerticalDrag
                | Microsoft.Xna.Framework.Input.Touch.GestureType.HorizontalDrag
                | Microsoft.Xna.Framework.Input.Touch.GestureType.DragComplete;
        }
        protected override void Initialize()
        {
            Game.Width = graphics.PreferredBackBufferWidth = 480;
            Game.Height = graphics.PreferredBackBufferHeight = 800;
            graphics.ApplyChanges();

            IconButton.SetContent(GraphicsDevice,
                Content.Load<SpriteFont>("Game/ButtonIconFont"),
                Content.Load<SpriteFont>("Game/ButtonTextFont"));
            TextButton.SetContent(GraphicsDevice, Content.Load<SpriteFont>("Game/ButtonTextFont"));

            State.Initialize(this.Content);

            State.RegisterState(new States.LoadingState());
            State.RegisterState(new States.MainMenuState());
            State.RegisterState(new States.LevelSelectionState(this.GraphicsDevice));
            State.RegisterState(new States.GameState());
            State.RegisterState(new States.DialogState(Dialog.Reset, this.GraphicsDevice,
                Content.Load<SpriteFont>("Dialog/TextFont"),
                (int)(Game.Width * 3f / 4f),(int)(Game.Width * 3f / 8f),
                "Are you sure you want to\nreset the level? Your history\nwill be lost.", "Reset", "Cancel"));
            State.RegisterState(new States.DialogState(Dialog.BackToMenu, this.GraphicsDevice,
                Content.Load<SpriteFont>("Dialog/TextFont"),
                (int)(Game.Width * 7f / 8f), (int)(Game.Width * 3f / 8f),
                "Are you sure you want to leave\nthe level and return to the main\nmenu? Your history will be lost.",
                "Proceed to Menu", "Return to Game"));
            State.RegisterState(new States.DialogState(Dialog.BackToLevelSelection, this.GraphicsDevice,
                Content.Load<SpriteFont>("Dialog/TextFont"),
                (int)(Game.Width * 3f / 4f), (int)(Game.Width * 3f / 8f),
                "Are you sure you want to leave\nthe level and return to level\nselection? Your history will\nbe lost.",
                "Proceed to Level Selection", "Return to Game"));

            this.LoadSavedGameData();

            //State.InitializeStates();

            base.Initialize();
        }
        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);

            State.LoadStateContent();
            State.InitializeStates();
        }
        protected override void UnloadContent() { }
        protected override void Update(GameTime gameTime)
        {
            if (Keyboard.GetState().IsKeyDown(Keys.Escape))
                base.Exit();

            State.UpdateState(gameTime.ElapsedGameTime.TotalSeconds);

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            if (takingScreenshot)
                GraphicsDevice.SetRenderTarget(screenshot);

            this.spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
            {
                State.DrawState(this.spriteBatch);
            }
            this.spriteBatch.End();

            if (takingScreenshot)
            {
                GraphicsDevice.SetRenderTarget(null);
                takingScreenshot = false;
                screenshotCallback(screenshot);
                screenshot = null;
                screenshotCallback = null;
            }

            base.Draw(gameTime);
        }
        public void Draw()
        {
            this.Draw(new GameTime());
        }

        public static void ExitGame()
        {
            TrafficTroubleGame.Exit();
        }

        private static bool takingScreenshot = false;
        private static RenderTarget2D screenshot;
        private static Action<Texture2D> screenshotCallback;
        public static void StartGetScreenshot(Action<Texture2D> callback)
        {
            takingScreenshot = true;
            screenshot = new RenderTarget2D(TrafficTroubleGame.GraphicsDevice, Width, Height);
            screenshotCallback = callback;
        }

        protected override void OnExiting(object sender, EventArgs args)
        {
            this.SaveCurrentGameData();
            base.OnExiting(sender, args);
        }
        protected override void OnDeactivated(object sender, EventArgs args)
        {
            this.SaveCurrentGameData();
            base.OnDeactivated(sender, args);
        }
        protected override void OnActivated(object sender, EventArgs args)
        {
            this.LoadSavedGameData();
            base.OnActivated(sender, args);
        }

        private void SaveCurrentGameData()
        {
            State currentState = State.GetCurrentState();
            // If the current game state is the actual game state, then save
            // the state of the current level so that it can be re-opened when
            // the application is restarted.
            if ((currentState.ID is Type && (Type)currentState.ID == typeof(GameState)) 
                || (currentState.ID is Dialog && ((Dialog)currentState.ID == Dialog.Reset
                    || (Dialog)currentState.ID == Dialog.BackToLevelSelection
                    || (Dialog)currentState.ID == Dialog.BackToMenu)))
            {
                GameState game = (GameState)currentState;

                IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication();
                if (storage.FileExists(SAVE_GAME_FILE))
                    storage.DeleteFile(SAVE_GAME_FILE);
                using (IsolatedStorageFileStream stream = storage.CreateFile(SAVE_GAME_FILE))
                {
                    stream.WriteByte(LEVEL_FORMAT_VERSION);
                    using (StreamWriter levelWriter = new StreamWriter(stream))
                    {
                        game.Level.WriteToStream(levelWriter, LEVEL_FORMAT_VERSION);
                        // also write history (# moves taken so far = history length)
                        History.WriteToStream(levelWriter, LEVEL_FORMAT_VERSION);
                    }
                }
            }
        }
        private void LoadSavedGameData()
        {
            IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication();
            if (storage.FileExists(SAVE_GAME_FILE))
            {
                GameState game = (GameState)State.States[typeof(GameState)];
                using (IsolatedStorageFileStream stream = storage.OpenFile(SAVE_GAME_FILE, FileMode.Open))
                {
                    byte formatVersion = (byte)stream.ReadByte();
                    using (StreamReader levelReader = new StreamReader(stream))
                    {
                        game.Level = Grid.FromStream(levelReader, formatVersion);
                        // also write history (# moves taken so far = history length)
                        History.ReadFromStream(levelReader, formatVersion, game.Level);
                    }
                }
                ((LoadingState)State.States[typeof(LoadingState)]).SwitchToGameState = true;
                ((LoadingState)State.States[typeof(LoadingState)]).GameLevel = game.Level;
            }
        }
    }
}
